package com.boarsoft.dao.sharding.gaussdb.filter;

import java.sql.SQLException;
import java.util.List;

import com.alibaba.druid.filter.FilterAdapter;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.proxy.jdbc.ConnectionProxy;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
import com.boarsoft.dao.sharding.bean.JdbcUrlInfo;
import com.boarsoft.dao.sharding.bean.ShardingParam;
import com.boarsoft.dao.sharding.datasource.DynamicDataSource;

import com.boarsoft.dao.sharding.util.JdbcUrlUtil;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 实现DRUID filter：拦截prepareStatement的构建，以修改SQL，为SQL添加hint
 */
public class DruidShardingFilter extends FilterAdapter {
	private static final Logger logger = LoggerFactory.getLogger(DruidShardingFilter.class);

	private static String HINT = "/*+db=%s_0100*/";
	private String jdbcUrl;

	public DruidShardingFilter(String jdbcUrl) {
		this.jdbcUrl = jdbcUrl;
	}

	@Override
	public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql)
			throws SQLException {
		try {
			return super.connection_prepareStatement(chain, connection, getRouteSql(sql));
		} catch (JSQLParserException e) {
			throw new SQLException(e);
		}
	}

	@Override
	public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql,
			int autoGeneratedKeys) throws SQLException {
		try {
			return super.connection_prepareStatement(chain, connection, getRouteSql(sql), autoGeneratedKeys);
		} catch (JSQLParserException e) {
			throw new SQLException(e);
		}
	}

	/**
	 * 修改SQL，为SQL添加hint
	 * 
	 * @param processSql
	 * @return
	 * @throws JSQLParserException
	 */
	public String getRouteSql(String processSql) throws JSQLParserException {
		ShardingParam sp = DynamicDataSource.getBinding();
		if (sp == null) {
			return processSql;
		}
		String uid = (String) sp.getKey();
		if ("-1".equals(uid)) {
			String databaseName = calDatabaseName(processSql);
			if (databaseName == null || "null".equals(databaseName) || "".equals(databaseName.trim())) {
				JdbcUrlInfo jdbcUrlInfo = JdbcUrlUtil.parseJdbcUrl(this.jdbcUrl);
				databaseName = jdbcUrlInfo.getDatabase();
				if (databaseName == null || "null".equals(databaseName) || "".equals(databaseName.trim())) {
					logger.warn("parse database name from jdbc url is empty, url: {}", this.jdbcUrl);
				}
			}
			return new StringBuilder().append(String.format(HINT, databaseName))
					.append(processSql).toString();
		}
		return processSql;
	}

	/**
	 * 获取表名，并修改（目前只支持单表）
	 * 
	 * @param processSql
	 * @return
	 * @throws JSQLParserException
	 */
	private String calTableName(String processSql) throws JSQLParserException {
		Statement statement = CCJSqlParserUtil.parse(processSql);
		TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
		List<String> tableList = tablesNamesFinder.getTableList(statement);
		return tableList.get(0);
	}

	/**
	 * 从SQL语句中获取数据库名
	 *
	 * @param processSql
	 * @return
	 * @throws JSQLParserException
	 */
	private String calDatabaseName(String processSql) throws JSQLParserException {
		// 构建解析器
		Select select = (Select) CCJSqlParserUtil.parse(processSql);
		// 获取select对象
		SelectBody selectBody = select.getSelectBody();
		// 解析sql
		PlainSelect plainSelect = (PlainSelect) selectBody;
		// 解析表对象
		Table table = (Table) plainSelect.getFromItem();
		// 返回数据库名称
		return table.getSchemaName();
	}
}